iT邦幫忙

2022 iThome 鐵人賽

DAY 26
1
Modern Web

真的好想離開 Vue 3 新手村 feat. CompositionAPI系列 第 26

Day 26: 在 Vue router - Navigation Guard 中使用 Pinia store 的小眉角

  • 分享至 

  • xImage
  •  

前言

在 route 中使用 Pinia store 時,不同的 route guard 常常會需要用到同一個 store,我一開始想要少寫一點程式碼XD,減少重複呼叫建立 store 實例,於是將 useStore 引入 route.js,在最外層的 scope 呼叫建立 store 實例,但這個方法會報錯。

會報錯的範例:

import { useHotelStore } from "@/stores/hotel.js";

const hotelStore = useHotelStore();
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    //略
    {
      path: "/rooms",
      name: "rooms",
      component: () => import("../views/RoomsView.vue"),
      async beforeEnter(to, from) {
        await hotelStore.getRooms();
      },
    },
    {
      path: "/room/:id",
      name: "room",
      component: () => import("../views/RoomView.vue"),
      async beforeEnter(to) {
        const roomID = to.params.id;
        await hotelStore.getRoom(roomID);
      },
    },
  ],
});

接下來會說明報錯的原因和正確的寫法。

報錯原因說明

報錯訊息:Uncaught ReferenceError: Cannot access 'useHotelStore' before initialization。


為什麼會報錯? 這個和 pinia 實例初始化的時機有關。

所有 stores 都依賴 pinia 這個實例,來共享所有的 store 實例
main.js 透過 createPinia() 建立 pinia 實例後,每次在元件中使用 store,其實都是 pinia 實例從 app 層 inject 到元件中,所以一般在元件內呼叫使用 store 都可以正常運作,因為元件的 setup() 都會在 app.use(createPinia()) 後才執行。

來看看 main.js,為什麼在 route.js 內不能直接呼叫 pinia 的 store:

import { createApp } from "vue";
import { createPinia } from "pinia";

import App from "./App.vue";
import router from "./router";

import "./assets/main.css";

const app = createApp(App);

app.use(createPinia());
app.use(router);

app.mount("#app");

在引入 router 檔案的時候(import router from "./router";),就會先執行 route.js,讀取到 useHotelStore 就會報錯,因為這時候 pinia 還沒有實體化,沒有辦法取到 useHotelStore

正確作法

那什麼時候會踩到坑?
想要在 Vue 元件以外的地方使用 Pinia store 的時候,最常見的情境應該就是在 navigation guard 內使用 store 的時候。

正確作法其實沒有什麼特殊的技巧,就是在每次定義 navigation guard 的 callback 時,都要乖乖呼叫 useStore 建立 store 實例。

正確示範如下:

import { useHotelStore } from "@/stores/hotel.js";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    //略
    {
      path: "/rooms",
      name: "rooms",
      component: () => import("../views/RoomsView.vue"),
      async beforeEnter(to, from) {
        const hotelStore = useHotelStore();
        await hotelStore.getRooms();
      },
    },
    {
      path: "/room/:id",
      name: "room",
      component: () => import("../views/RoomView.vue"),
      async beforeEnter(to) {
        const roomID = to.params.id;
        const hotelStore = useHotelStore();
        await hotelStore.getRoom(roomID);
      },
    },
  ],
});

結尾

一開始踩到雷的時候蠻困擾的,還是看 stackoverflow 去解 bug,結果,後來發現官方文件根本就有寫哈哈哈(請見參考資料),只是一開始學習 Pinia 時,還不太懂這個主旨的含意跟情境,所以就先跳過這個篇章了 ╥﹏╥
果然...還是要乖乖看官方文件啊~~

參考資料


上一篇
Day 25: 來發 API 吧!Pinia 語法學完馬上用
下一篇
Day 27: 從 VueUse - useAsyncState 認識 Composable 和非同步處理
系列文
真的好想離開 Vue 3 新手村 feat. CompositionAPI31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言